unit ICore.Log;
//{$UNDEF UNICODE}
{$UNDEF AUTOLOG}

interface
uses Windows,
      ICore.Utils,
      {$IFNDEF ICORE}
      SysUtils,
      {$ENDIF}
      ICore.Stream;

//type TStr = AnsiString;
const
  M_OK = MB_OK;
  M_OKCANCEL = MB_OKCANCEL;
  M_ABORTRETRYIGNORE = MB_ABORTRETRYIGNORE;
  M_YESNOCANCEL = MB_YESNOCANCEL;
  M_YESNO = MB_YESNO;
  M_RETRYCANCEL = MB_RETRYCANCEL;
  M_ICONQUESTION = MB_ICONQUESTION;
  M_ICONERROR = MB_ICONERROR;

type
  TLogMessageType = (mtNone, mtMessage, mtWarning, mtError, mtInfo, mtConsole);

{$REGION 'TLog = record'}
  TLog = record
  private
    class var
      FLogFile: TStreamFile;
      {$IFDEF TOOLS}
        FMessage: string;
      {$ENDIF}
      FFileName: string;
      FDump: string;
      FListDumpEnable: Boolean;
      FListDumpCapacity: LongInt;
      FListDumpCount: LongInt;
    class procedure RawPrint(const Text: string;
                              const MessageType: TLogMessageType = mtNone); static; //inline;
    class function GetDump: string; static;
  public
    class function Init(const pFileName: string;
                        const Title: string = 'StrangeLetGames';
                        const Link: string = 'http://StrangeLetGames.com';
                        const EnableListDump: Boolean = False): Boolean; static;
    class procedure Free; static;
    class procedure Print(const Text: string;
                            const MessageType: TLogMessageType = mtMessage;
                            const TimeStamp: Boolean = True); static;
    class procedure PrintHR; static;
    class procedure Warning(const Text: string); overload; static;
    class procedure Warning(const Text: AnsiString); overload; static;
    class procedure Info(const Text: string); overload; static;
    class procedure Info(const Text: AnsiString); overload; static;
    class procedure Error(const Text: string); overload; static;
    class procedure Error(const Text: AnsiString); overload; static;
    class procedure Message_(const Text: string; const Title: string;
                                const ID: Cardinal = MB_OK); static;// inline;
    class procedure Console(const Text: string); static;// inline;
    class function Valid: Boolean; static; inline;

    {$IFDEF TOOLS}
      class property LogMessage: string read FMessage write FMessage;
    {$ENDIF}
    class property FileName: string read FFileName;
    class property Dump: string read GetDump;
  end;
{$ENDREGION}

{$IFNDEF ICORE}
const
  CR = #13;
  LF = #10;
  CRLF  = #13#10;
  TAB   = #9;

var
  Log: TLog;
{$ENDIF}

implementation
{$IFDEF ICORE}
uses ICore;
{$ENDIF}

{$REGION 'System'}

{$IFNDEF ICORE}
function GetCurrentTime: String;
var
  SystemTime: TSystemTime;
begin
  GetLocalTime(SystemTime);
  Result := IntToStr(SystemTime.wHour) + ':' +
            IntToStr(SystemTime.wMinute) + ':' +
            IntToStr(SystemTime.wSecond) + '.' +
            IntToStr(SystemTime.wMilliseconds);
end;
{$ENDIF}

{$ENDREGION}

{$REGION 'TLog'}
class procedure TLog.RawPrint(const Text: string; const MessageType: TLogMessageType = mtNone);
var
  LogStr: string;
begin
  {$IFDEF TOOLS}
    FMessage := Text;
  {$ENDIF}
  case MessageType of
    mtMessage:  LogStr := '<div class="m">';
    mtWarning:  LogStr := '<div class="w">';
    mtError:    LogStr := '<div class="e">';
    mtInfo:     LogStr := '<div class="i">';
    mtConsole:  LogStr := '<div class="c">';
    else LogStr := '';
  end;
  LogStr := LogStr + Text;
  if (MessageType <> mtNone) then
   LogStr := LogStr + '</div>';
  LogStr := LogStr + CRLF;
  if FLogFile.Valid then
   FLogFile.Write(LogStr[1], Length(LogStr) * SizeOf(LogStr[1]));
  if (IsConsole) and
      ((MessageType = mtConsole) or
        (MessageType = mtWarning) or
        (MessageType = mtError) or
        (MessageType = mtMessage)) then
   case MessageType of
//     mtNone: ;
//     mtMessage: Writeln(Text + ' (Message)');
     mtWarning: Writeln(Text + ' (Warning)');
     mtError: Writeln(Text + ' (Error)');
//     mtInfo: ;
     mtConsole: Writeln(Text);
   end;
  FDump := FDump + LogStr;
  FListDumpCount := FListDumpCount + 1;
end;

class function TLog.GetDump: string;
begin
  Result := FDump;
end;

class function TLog.Init(const pFileName: string;
                          const Title: string = 'StrangeLetGames';
                          const Link: string = 'http://StrangeLetGames.com';
                          const EnableListDump: Boolean = False): Boolean;
{$IFDEF UNICODE}
const
  UTF16_HEADER: array [0..1] of Byte = ($FF, $FE);
{$ENDIF}
var
  CSSFile: TStreamFile;
  tmpstr: AnsiString;
begin
  FDump := '';
  FListDumpEnable := False;

  FLogFile := TStreamFile.Create;
  FFileName := pFileName;
  if Length(pFileName) > 0 then
   FLogFile.Open(pFileName, True);
  Result := FLogFile.Valid;
  if Result then begin
    {$IFDEF UNICODE}
      FLogFile.Write(UTF16_HEADER, 2);
    {$ENDIF}
    RawPrint('<?xml version="1.0" encoding="utf-8" standalone="yes"?>');
    RawPrint('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">');
    RawPrint('<html><head>');
    {$IFDEF ICORE}
    RawPrint('<title>' + TUtils.ExtractFileNameNoExt(EngineFile) + ' log file</title>');
    {$ELSE}
    RawPrint('<title>' + ExtractFileName(pFileName) + ' log file</title>');
    {$ENDIF}
    {$IFDEF ICORE}
    if TUtils.ExistFile('style.css') then begin
    {$ELSE}
    if FileExists('style.css') then begin    
    {$ENDIF}
      CSSFile := TStreamFile.Create;
      try
        CSSFile.Open('style.css');
        if (CSSFile.Valid) then begin
          SetLength(tmpstr, CSSFile.Size);
          CSSFile.Read(tmpstr[1], CSSFile.Size);
          tmpstr := '<style type="text/css">' + CRLF + tmpstr + '</style>' + CRLF;
          RawPrint(string(tmpstr));
          tmpstr := '';
        end;
      except
        RawPrint('<link href="style.css" rel="stylesheet" type="text/css">');
      end;
      CSSFile.Free;
    end else begin
      RawPrint('<style type="text/css">');
      RawPrint('body { background: #1a242a; color: #b4c8d2; margin-right: 20px; margin-left: 20px; font-size: 14px; font-family: Arial, sans-serif, sans; }');
      RawPrint('a { text-decoration: none; }');
      RawPrint('a:link { color: #b4c8d2; }');
      RawPrint('a:active { color: #ff9900; }');
      RawPrint('a:visited { color: #b4c8d2; }');
      RawPrint('a:hover { color: #ff9900; }');
      RawPrint('h1 { text-align: center; }');
      RawPrint('h2 { color: #ffffff; }');
      RawPrint('hr {');
      RawPrint(TAB + 'height: 1px;');
      RawPrint(TAB + 'color: #ff9933;');
      RawPrint(TAB + 'background-color: #b4d2bd;');
      RawPrint(TAB + 'border: 0;');
      RawPrint(TAB + 'margin: .2em 0 .2em 0;');
      RawPrint('}');
      RawPrint('.m, .w, .e, .f, .i, .c { padding: 3px; overflow: auto; }');
      RawPrint('.m { color: #b4c8d2; }');
      RawPrint('.w { background-color: #839ca7; color: #1a242a; }');
      RawPrint('.e { background-color: #ff9933; color: #1a242a; }');
      RawPrint('.f { background-color: #ff0000; color: #1a242a; }');
      RawPrint('.i { color: #b4d2bd; }');
      RawPrint('.c { background-color: #000000; color: #1a242a; }');
      RawPrint('</style>');
    end;
    RawPrint('</head><body>');
    RawPrint('<h1><a href="' + Link + '">' + Title + '</a></h1>');
    Print('Log: Init "' + pFileName + '"', mtMessage, False);
  end;
end;

class procedure TLog.Free;
begin
  Print('Log: Free', mtMessage, False);
  RawPrint('</body></html>');
  FLogFile.Free;
end;

class procedure TLog.Print(const Text: string;
                            const MessageType: TLogMessageType = mtMessage;
                            const TimeStamp: Boolean = True);
begin
  if TimeStamp then
   {$IFDEF ICORE}
   RawPrint('[' + TUtils.GetCurrentTime + '] ' + Text, MessageType)
   {$ELSE}
   RawPrint('[' + GetCurrentTime + '] ' + Text, MessageType)
   {$ENDIF}
  else
   RawPrint(Text, MessageType);
end;

class procedure TLog.PrintHR;
begin
  RawPrint('<hr>');
end;

class procedure TLog.Warning(const Text: string);
begin
  Print(Text, mtWarning, True);
end;

class procedure TLog.Warning(const Text: AnsiString);
begin
  Print(string(Text), mtWarning, True);
end;

class procedure TLog.Info(const Text: string);
begin
  Print(Text, mtInfo, True);
end;

class procedure TLog.Info(const Text: AnsiString);
begin
  Print(string(Text), mtInfo, True);
end;

class procedure TLog.Error(const Text: string);
begin
  Print(Text, mtError, True);
//  Log.Message_('Error: ' + Text, 'Error', MB_ICONHAND);
end;

class procedure TLog.Error(const Text: AnsiString);
begin
  Print(string(Text), mtError, True);
//  Log.Message_('Error: ' + string(Text), 'Error', MB_ICONHAND);
end;

class procedure TLog.Message_(const Text: string; const Title: string; const ID: Cardinal = MB_OK);
begin
  {$IFDEF ICORE}
  Print(Text + '[Caption: "' + Title + '" ID: "' + TUtils.IntToStr(ID) + '"', mtMessage, True);
  {$ELSE}
  Print(Text + '[Caption: "' + Title + '" ID: "' + IntToStr(ID) + '"', mtMessage, True);
  {$ENDIF}
{$IFDEF UNICODE}
  MessageBox(0, PChar(Text), PChar(Title), ID);
{$ELSE}
  MessageBoxA(0, PAnsiChar(Text), PAnsiChar(Title), ID);
{$ENDIF}
end;

class procedure TLog.Console(const Text: string);
begin
  Print(Text, mtConsole, True);
end;

class function TLog.Valid: Boolean;
begin
  if FLogFile <> nil then
   Result := FLogFile.Valid
  else
   Result := False;
end;
{$ENDREGION}

{$IFDEF AUTOLOG}
  {$IFNDEF ICORE}
initialization
  Log.Init('Log.html');

finalization
  Log.Free;
  {$ENDIF}
{$ENDIF}

end.
